using GameFramework.Localization;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using UnityEngine;

namespace LS.Localization
{
    public static class LanguageXML
    {
        public static readonly string Attribute_Language = "Language";
        public static readonly string Attribute_Key = "Key";
        public static readonly string Attribute_Value = "Value";
        public static readonly string Attribute_IsLinked = "IsLinked";

        public static readonly string Element_Dictionary = "Dictionary";
        public static readonly string Element_String = "String";

        public static XmlElement CreateChild(this XmlElement parent, string name)
        {
            var node = parent.OwnerDocument.CreateElement(name);
            parent.AppendChild(node);
            return node;
        }

        public static XmlElement AddDictionaryPanel(this XmlDocument doc, Language language)
        {
            XmlElement root = doc.SelectSingleNode(Element_Dictionary) as XmlElement;
            if (root == null)
            {
                root = doc.CreateElement(Element_Dictionary);
                root.SetAttribute(Attribute_Language, language.ToString());
                doc.AppendChild(root);
            }
            return root;
        }

        public static XmlElement GetDictionaryPanel(this XmlDocument doc)
        {
            XmlElement root = doc.SelectSingleNode(Element_Dictionary) as XmlElement;
            return root;
        }

        public static XmlElement AddLanguageContent(this XmlElement languagePanel, string key, string value, int? isLinked = null)
        {
            XmlElement node = languagePanel.GetLanguageContent(key);
            if (node == null)
            {
                node = languagePanel.CreateChild(Element_String);
            }
            node.SetAttribute(Attribute_Key, key);
            node.SetAttribute(Attribute_Value, value);
            if (isLinked.HasValue)
                node.SetAttribute(Attribute_IsLinked, isLinked.Value.ToString());
            return node;
        }

        public static XmlElement GetLanguageContent(this XmlElement languagePanel, string key)
        {
            XmlElement node = (languagePanel.FirstChild as XmlElement).FirstLanguageContent();
            do
            {
                if (node == null) break;
                string value = node.GetAttribute(Attribute_Key);

                if (!string.IsNullOrEmpty(value) && value == key)
                {
                    break;
                }

                node = node.NextLanguageContent();
            } while (node != null);
            return node;
        }

        public static XmlElement FirstLanguageContent(this XmlElement element)
        {
            XmlElement languagePanel = null;
            XmlElement node = element;
            do
            {
                if (node == null) break;
                if (node.Name == Element_String)
                {
                    languagePanel = node;
                }
                node = node.PreviousSibling as XmlElement;
            } while (node != null);

            if (languagePanel == null)
            {
                node = element.NextSibling as XmlElement;
                do
                {
                    if (node == null) break;
                    if (node.Name == Element_String)
                    {
                        languagePanel = node;
                    }
                    node = node.NextSibling as XmlElement;
                } while (node != null);
            }

            return languagePanel;
        }

        public static XmlElement NextLanguageContent(this XmlElement element)
        {
            XmlElement node = element.NextSibling as XmlElement;
            do
            {
                if (node == null) break;

                if (node.Name == Element_String)
                {
                    return node;
                }

                node = node.NextSibling as XmlElement;
            } while (node != null);

            return null;
        }
    }

    public class LanguageXMLReader
    {
        private LanguageDataTab m_LanguageDataTable;

        public Language Language => m_LanguageDataTable?.Language ?? Language.Unspecified;

        public string[] Keys => m_LanguageDataTable?.Keys ?? new string[0];

        public LanguageContent[] Values => m_LanguageDataTable?.Values ?? new LanguageContent[0];

        public LanguageDataTab Data => m_LanguageDataTable;

        public int Count => m_LanguageDataTable?.Count ?? 0;

        public LanguageXMLReader() : this(null)
        {

        }

        public LanguageXMLReader(string text)
        {
            if (!string.IsNullOrEmpty(text))
            {
                Read(text);
            }
        }

        public void Read(string xml)
        {
            if (string.IsNullOrEmpty(xml))
                return;

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            XmlElement xmlRoot = doc.GetDictionaryPanel();
            Debug.Log($"Root is null? {xmlRoot == null}");
            if (xmlRoot == null) return;

            Language language = Language.Unspecified;
            string languageStr = xmlRoot.GetAttribute(LanguageXML.Attribute_Language);

            if (!Enum.TryParse<Language>(languageStr, out language))
                throw new Exception("Language read fail");

            m_LanguageDataTable = new LanguageDataTab(language);

            if (xmlRoot.HasChildNodes)
            {
                XmlElement languageContent = (xmlRoot.FirstChild as XmlElement).FirstLanguageContent();
                do
                {
                    if (languageContent == null) break;

                    string key = languageContent.GetAttribute(LanguageXML.Attribute_Key);
                    string value = languageContent.GetAttribute(LanguageXML.Attribute_Value);
                    string isLinkedString = languageContent.GetAttribute(LanguageXML.Attribute_IsLinked);
                    if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
                    {
                        int? isLinked = null;
                        if (!string.IsNullOrEmpty(isLinkedString) && int.TryParse(isLinkedString, out int ilv))
                        {
                            isLinked = ilv;
                        }
                        m_LanguageDataTable.SetValue(key, value, isLinked);
                    }

                    languageContent = languageContent.NextLanguageContent();
                } while (languageContent != null);
            }
        }

        public void Clear()
        {
            m_LanguageDataTable.Clear();
        }

        public bool Contains(Language language)
        {
            if (Language == Language.Unspecified)
                return false;

            return Language == language;
        }
    }

    public class LanguageXMLCreater
    {
        private LanguageDataTab m_LanguageDataTable = null;

        public Language Language => m_LanguageDataTable?.Language ?? Language.Unspecified;

        public string[] Keys => m_LanguageDataTable?.Keys ?? new string[0];

        public LanguageContent[] Values => m_LanguageDataTable?.Values ?? new LanguageContent[0];

        public LanguageDataTab Data => m_LanguageDataTable;

        public int Count => m_LanguageDataTable?.Count ?? 0;

        public LanguageXMLCreater(): this(Language.Unspecified)
        {
            
        }

        public LanguageXMLCreater(LanguageXMLReader reader): this(reader.Language)
        {
            AddFromReader(reader);
        }

        public LanguageXMLCreater(Language language)
        {
            m_LanguageDataTable = new LanguageDataTab(language);
        }

        public void AddFromReader(LanguageXMLReader reader)
        {
            foreach (var item in reader.Values)
            {
                AddContent(item);
            }
        }

        public void AddContent(string key, string value, int? isLinked = null)
        {
            m_LanguageDataTable.SetValue(key, value, isLinked);
        }

        public void AddContent(LanguageContent content)
        {
            m_LanguageDataTable.SetValue(content);
        }

        public LanguageContent GetContent(string key)
        {
            return m_LanguageDataTable.GetValue(key);
        }

        public void RemoveContent(string key)
        {
            m_LanguageDataTable.Remove(key);
        }

        public void Clear()
        {
            m_LanguageDataTable.Clear();
        }

        public override string ToString()
        {
            XmlDocument doc = new XmlDocument();
            XmlElement root = doc.AddDictionaryPanel(Language);

            foreach (var item in Values)
            {
                XmlElement element = root.AddLanguageContent(item.Key,item.Value,item.IsLinked);
            }
            return doc.ToString();
        }

        public static LanguageXMLCreater FromReader(LanguageXMLReader reader)
        {
            return new LanguageXMLCreater(reader);
        }
    }

    public class LanguageContent
    {
        private string m_Key;
        private string m_Value;
        private int? m_IsLinked;

        public string Key => m_Key;
        public string Value => m_Value;
        public int? IsLinked => m_IsLinked;

        public LanguageContent(
            string key = null,
            string value = null,
            int? isLinked = null
            )
        {
            this.m_Key = key;
            this.m_Value = value;
            this.m_IsLinked = isLinked;
        }

        public bool GetIsLinkedBool()
        {
            return m_IsLinked.GetValueOrDefault() == 1;
        }
    }

    public class LanguageDataTab
    {
        private Language m_Language;
        private Dictionary<string, LanguageContent> m_Contents;

        public int Count => m_Contents?.Count ?? 0;

        public Language Language => m_Language;

        public string[] Keys => m_Contents.Keys.ToArray();

        public LanguageContent[] Values => m_Contents.Values.ToArray();

        public LanguageDataTab(Language language)
        {
            this.m_Language = language;
            m_Contents = new Dictionary<string, LanguageContent>();
        }

        public void SetValue(string key, string value, int? isLinked = null)
        {
            m_Contents[key] = new LanguageContent(key, value, isLinked);
        }

        public void SetValue(LanguageContent content)
        {
            if (string.IsNullOrEmpty(content.Key))
                throw new Exception("LanguageContent should set key before you add it!");

            m_Contents[content.Key] = content;
        }

        public LanguageContent GetValue(string key)
        {
            if (m_Contents.TryGetValue(key, out var value))
            {
                return value;
            }
            return null;
        }

        public void Remove(string key)
        {
            m_Contents.Remove(key);
        }

        public void Remove(LanguageContent content)
        {
            m_Contents.Remove(content.Key);
        }

        public void Clear()
        {
            m_Contents.Clear();
        }

        public bool Contains(string key)
        {
            return m_Contents.ContainsKey(key);
        }
    }
}
